#include <iostream>
#include <memory>
#include <string>
#include <sys/epoll.h>
#include "Socket.hpp"

using namespace Socket_ns;

const static int gnum = 1024;
const static int gDefaultFd = -1;

class EpollServer
{
private:
    // num个文件描述符就绪
    void HandlerEvents(int num)
    {
        for (int i = 0; i < num; i++)
        {
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            if (revents & EPOLLIN)
            {
                if (sockfd == _listenSock->SockFd())
                {
                    // 如果是监听套接字就绪
                    InetAddr clientAddr;
                    int newfd = _listenSock->Accepter(&clientAddr);
                    if (newfd < 0)
                    {
                        LOG(WARNING, "accept newfd error");
                        continue;
                    }
                    // 添加到epoll模型中
                    struct epoll_event ev;
                    ev.events = EPOLLIN;
                    ev.data.fd = newfd;
                    epoll_ctl(_epFd, EPOLL_CTL_ADD, newfd, &ev);
                    LOG(DEBUG, "_listensock ready, accept done, epoll_ctl done, newfd is: %d, from %s:%d",
                        newfd, clientAddr.IP().c_str(), clientAddr.Port());
                }
                else
                {
                    // 处理读
                    char buffer[1024];
                    int rdBytes = ::recv(sockfd, buffer, sizeof(buffer) - 1, 0);
                    if(rdBytes < 0)   
                    {
                        LOG(WARNING, "recv error...");
                        // 从epoll模型中删除的文件描述符一定要是合法的，因此不能先关闭再删除
                        ::epoll_ctl(_epFd, EPOLL_CTL_DEL, sockfd, nullptr); 
                        ::close(sockfd);
                    }
                    else if(rdBytes == 0)
                    {
                        LOG(WARNING, "对端连接关闭...");
                        ::epoll_ctl(_epFd, EPOLL_CTL_DEL, sockfd, nullptr);
                        ::close(sockfd);
                    }
                    else
                    {
                        buffer[rdBytes] = 0;
                        std::cout << "client say@ " << buffer << std::endl;
                        std::string response = "[server echo]@ ";
                        response += buffer;
                        send(sockfd, response.c_str(), response.size(), 0);
                    }
                }
            }
        }
    }

public:
    EpollServer(uint16_t port)
        : _port(port), _listenSock(std::make_unique<TCPSocket>()), _timeout(1000), _epFd(-1)
    {
        // 1. 创建监听套接字
        InetAddr addr("0", _port);
        _listenSock->BuildListenSocket(addr);

        // 2. 创建epoll模型
        _epFd = epoll_create(128);
        if (_epFd < 0)
        {
            LOG(FATAL, "epoll fd create error!");
            exit(5);
        }

        // 3. 关心监听套接字的读事件
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listenSock->SockFd(); // 在有事件就绪时，用于区分是哪个文件描述符就绪
        epoll_ctl(_epFd, EPOLL_CTL_ADD, _listenSock->SockFd(), &ev);
    }

    void Loop()
    {
        while (true)
        {
            int n = epoll_wait(_epFd, _revs, gnum, _timeout);
            switch (n)
            {
            case -1:
                LOG(WARNING, "epoll error...");
                break;
            case 0:
                LOG(INFO, "epoll timeout...");
                break;
            default:
                // 有文件描述符就绪
                // LOG(DEBUG, "Event Happen...");
                HandlerEvents(n);
                break;
            }
        }
    }

    ~EpollServer()
    {
        if (_epFd > 0)
            ::close(_epFd);
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;
    struct epoll_event _revs[gnum];
    int _epFd;
    int _timeout;
};